`timescale 1ps/1ps

`define RESPONSE_OKAY   2'b00
`define RESPONSE_EXOKAY 2'b01
`define RESPONSE_SLVERR 2'b10
`define RESPONSE_DECERR 2'b11

`define BURST_TYPE_FIXED 2'b00
`define BURST_TYPE_INCR  2'b01
`define BURST_TYPE_WRAP  2'b10
`define LOCK_TYPE_NORMAL    1'b0
`define LOCK_TYPE_EXCLUSIVE 1'b1

`define M1_DATA_BUS_WIDTH 32
`define M1_ID_BUS_WIDTH 1
`define M1_USER_BUS_WIDTH 1

module tb;

  import "DPI-C" context task StartSoftware();
  export "DPI-C" task RegReadByte;
  export "DPI-C" task RegReadWord;
  export "DPI-C" task RegReadDWord;
  export "DPI-C" task RegWriteByte;
  export "DPI-C" task RegWriteWord;
  export "DPI-C" task RegWriteDWord;
  export "DPI-C" task MemRead;
  export "DPI-C" task MemWrite;
  export "DPI-C" task MemReadDW;
  export "DPI-C" task MemWriteDW;

  integer            i;

  reg SYS_CLK;
  reg SYS_RST;

  wire ACLK;
  wire ARESETn;
  
  eps
  eps
  (
    .SYS_CLK      (SYS_CLK),
    .SYS_RST      (SYS_RST),
    .CLK125       (),
    .CLK250       (),
    .LOCKED       (),
    .ACLK         (ACLK),
    .ARESETn      (ARESETn),

    .M_AN_AWADDR  (an.S_AWADDR  ),
    .M_AN_AWVALID (an.S_AWVALID ),
    .M_AN_AWREADY (an.S_AWREADY ),
    .M_AN_WDATA   (an.S_WDATA   ),
    .M_AN_WSTRB   (an.S_WSTRB   ),
    .M_AN_WVALID  (an.S_WVALID  ),
    .M_AN_WREADY  (an.S_WREADY  ),
    .M_AN_BRESP   (an.S_BRESP   ),
    .M_AN_BVALID  (an.S_BVALID  ),
    .M_AN_BREADY  (an.S_BREADY  ),
    .M_AN_ARADDR  (an.S_ARADDR  ),
    .M_AN_ARVALID (an.S_ARVALID ),
    .M_AN_ARREADY (an.S_ARREADY ),
    .M_AN_RDATA   (an.S_RDATA   ),
    .M_AN_RRESP   (an.S_RRESP   ),
    .M_AN_RVALID  (an.S_RVALID  ),
    .M_AN_RREADY  (an.S_RREADY  ),

    .M_EX_AWADDR  (ex.S_AWADDR  ),
    .M_EX_AWVALID (ex.S_AWVALID ),
    .M_EX_AWREADY (ex.S_AWREADY ),
    .M_EX_WDATA   (ex.S_WDATA   ),
    .M_EX_WSTRB   (ex.S_WSTRB   ),
    .M_EX_WVALID  (ex.S_WVALID  ),
    .M_EX_WREADY  (ex.S_WREADY  ),
    .M_EX_BRESP   (ex.S_BRESP   ),
    .M_EX_BVALID  (ex.S_BVALID  ),
    .M_EX_BREADY  (ex.S_BREADY  ),
    .M_EX_ARADDR  (ex.S_ARADDR  ),
    .M_EX_ARVALID (ex.S_ARVALID ),
    .M_EX_ARREADY (ex.S_ARREADY ),
    .M_EX_RDATA   (ex.S_RDATA   ),
    .M_EX_RRESP   (ex.S_RRESP   ),
    .M_EX_RVALID  (ex.S_RVALID  ),
    .M_EX_RREADY  (ex.S_RREADY  ),

    .S0_AN_AWID    (an.M_DN_AWID    ),
    .S0_AN_AWADDR  (an.M_DN_AWADDR  ),
    .S0_AN_AWLEN   (an.M_DN_AWLEN   ),
    .S0_AN_AWSIZE  (an.M_DN_AWSIZE  ),
    .S0_AN_AWBURST (an.M_DN_AWBURST ),
    .S0_AN_AWVALID (an.M_DN_AWVALID ),
    .S0_AN_AWREADY (an.M_DN_AWREADY ),
    .S0_AN_WDATA   (an.M_DN_WDATA   ),
    .S0_AN_WSTRB   (an.M_DN_WSTRB   ),
    .S0_AN_WLAST   (an.M_DN_WLAST   ),
    .S0_AN_WVALID  (an.M_DN_WVALID  ),
    .S0_AN_WREADY  (an.M_DN_WREADY  ),
    .S0_AN_BID     (an.M_DN_BID     ),
    .S0_AN_BRESP   (an.M_DN_BRESP   ),
    .S0_AN_BVALID  (an.M_DN_BVALID  ),
    .S0_AN_BREADY  (an.M_DN_BREADY  ),

    .S1_AN_AWID    (an.M_UP_AWID    ),
    .S1_AN_AWADDR  (an.M_UP_AWADDR  ),
    .S1_AN_AWLEN   (an.M_UP_AWLEN   ),
    .S1_AN_AWSIZE  (an.M_UP_AWSIZE  ),
    .S1_AN_AWBURST (an.M_UP_AWBURST ),
    .S1_AN_AWVALID (an.M_UP_AWVALID ),
    .S1_AN_AWREADY (an.M_UP_AWREADY ),
    .S1_AN_WDATA   (an.M_UP_WDATA   ),
    .S1_AN_WSTRB   (an.M_UP_WSTRB   ),
    .S1_AN_WLAST   (an.M_UP_WLAST   ),
    .S1_AN_WVALID  (an.M_UP_WVALID  ),
    .S1_AN_WREADY  (an.M_UP_WREADY  ),
    .S1_AN_BID     (an.M_UP_BID     ),
    .S1_AN_BRESP   (an.M_UP_BRESP   ),
    .S1_AN_BVALID  (an.M_UP_BVALID  ),
    .S1_AN_BREADY  (an.M_UP_BREADY  ),

    .S_EX_ARID    (ex.M_ARID    ),
    .S_EX_ARADDR  (ex.M_ARADDR  ),
    .S_EX_ARLEN   (ex.M_ARLEN   ),
    .S_EX_ARSIZE  (ex.M_ARSIZE  ),
    .S_EX_ARBURST (ex.M_ARBURST ),
    .S_EX_ARVALID (ex.M_ARVALID ),
    .S_EX_ARREADY (ex.M_ARREADY ),
    .S_EX_RID     (ex.M_RID     ),
    .S_EX_RDATA   (ex.M_RDATA   ),
    .S_EX_RRESP   (ex.M_RRESP   ),
    .S_EX_RLAST   (ex.M_RLAST   ),
    .S_EX_RVALID  (ex.M_RVALID  ),
    .S_EX_RREADY  (ex.M_RREADY  ),
    .S_EX_AWID    (ex.M_AWID    ),
    .S_EX_AWADDR  (ex.M_AWADDR  ),
    .S_EX_AWLEN   (ex.M_AWLEN   ),
    .S_EX_AWSIZE  (ex.M_AWSIZE  ),
    .S_EX_AWBURST (ex.M_AWBURST ),
    .S_EX_AWVALID (ex.M_AWVALID ),
    .S_EX_AWREADY (ex.M_AWREADY ),
    .S_EX_WDATA   (ex.M_WDATA   ),
    .S_EX_WSTRB   (ex.M_WSTRB   ),
    .S_EX_WLAST   (ex.M_WLAST   ),
    .S_EX_WVALID  (ex.M_WVALID  ),
    .S_EX_WREADY  (ex.M_WREADY  ),
    .S_EX_BID     (ex.M_BID     ),
    .S_EX_BRESP   (ex.M_BRESP   ),
    .S_EX_BVALID  (ex.M_BVALID  ),
    .S_EX_BREADY  (ex.M_BREADY  )
  );

  AxisRecordPlayer
  ex(
    .CLK        (eps.CLK125),
    .RST        (!eps.LOCKED),
  
    .S_AWADDR   (eps.M_EX_AWADDR ),
    .S_AWVALID  (eps.M_EX_AWVALID),
    .S_AWREADY  (eps.M_EX_AWREADY),
    .S_WDATA    (eps.M_EX_WDATA  ),
    .S_WSTRB    (eps.M_EX_WSTRB  ),
    .S_WVALID   (eps.M_EX_WVALID ),
    .S_WREADY   (eps.M_EX_WREADY ),
    .S_BRESP    (eps.M_EX_BRESP  ),
    .S_BVALID   (eps.M_EX_BVALID ),
    .S_BREADY   (eps.M_EX_BREADY ),
    .S_ARADDR   (eps.M_EX_ARADDR ),
    .S_ARVALID  (eps.M_EX_ARVALID),
    .S_ARREADY  (eps.M_EX_ARREADY),
    .S_RDATA    (eps.M_EX_RDATA  ),
    .S_RRESP    (eps.M_EX_RRESP  ),
    .S_RVALID   (eps.M_EX_RVALID ),
    .S_RREADY   (eps.M_EX_RREADY ),

    .S_TVALID   (ex.M_TVALID),
    .S_TREADY   (ex.M_TREADY),
    .S_TDATA    (ex.M_TDATA ),
    .S_TSTRB    (ex.M_TSTRB ),
    .S_TUSER    (ex.M_TUSER ),
    .S_TLAST    (ex.M_TLAST ),
  
    .M_TVALID   (ex.S_TVALID),
    .M_TREADY   (ex.S_TREADY),
    .M_TDATA    (ex.S_TDATA ),
    .M_TSTRB    (ex.S_TSTRB ),
    .M_TUSER    (ex.S_TUSER ),
    .M_TLAST    (ex.S_TLAST ),

    .M_AWID     (eps.S_EX_AWID),
    .M_AWADDR   (eps.S_EX_AWADDR ),
    .M_AWLEN    (eps.S_EX_AWLEN  ),
    .M_AWSIZE   (eps.S_EX_AWSIZE ),
    .M_AWBURST  (eps.S_EX_AWBURST),
    .M_AWVALID  (eps.S_EX_AWVALID),
    .M_AWREADY  (eps.S_EX_AWREADY),
    .M_WDATA    (eps.S_EX_WDATA  ),
    .M_WSTRB    (eps.S_EX_WSTRB  ),
    .M_WLAST    (eps.S_EX_WLAST  ),
    .M_WVALID   (eps.S_EX_WVALID ),
    .M_WREADY   (eps.S_EX_WREADY ),
    .M_BID      (eps.S_EX_BID),
    .M_BRESP    (eps.S_EX_BRESP  ),
    .M_BVALID   (eps.S_EX_BVALID ),
    .M_BREADY   (eps.S_EX_BREADY ),
    .M_ARID     (eps.S_EX_ARID),
    .M_ARADDR   (eps.S_EX_ARADDR ),
    .M_ARLEN    (eps.S_EX_ARLEN  ),
    .M_ARSIZE   (eps.S_EX_ARSIZE ),
    .M_ARBURST  (eps.S_EX_ARBURST),
    .M_ARVALID  (eps.S_EX_ARVALID),
    .M_ARREADY  (eps.S_EX_ARREADY),
    .M_RID      (eps.S_EX_RID),
    .M_RDATA    (eps.S_EX_RDATA  ),
    .M_RRESP    (eps.S_EX_RRESP  ),
    .M_RLAST    (eps.S_EX_RLAST  ),
    .M_RVALID   (eps.S_EX_RVALID ),
    .M_RREADY   (eps.S_EX_RREADY )
  );
  
  capture_module
  an(
    .CLK        (eps.CLK125),
    .RST        (!eps.LOCKED),
  
    .S_AWADDR   (eps.M_AN_AWADDR ),
    .S_AWVALID  (eps.M_AN_AWVALID),
    .S_AWREADY  (eps.M_AN_AWREADY),
    .S_WDATA    (eps.M_AN_WDATA  ),
    .S_WSTRB    (eps.M_AN_WSTRB  ),
    .S_WVALID   (eps.M_AN_WVALID ),
    .S_WREADY   (eps.M_AN_WREADY ),
    .S_BRESP    (eps.M_AN_BRESP  ),
    .S_BVALID   (eps.M_AN_BVALID ),
    .S_BREADY   (eps.M_AN_BREADY ),
    .S_ARADDR   (eps.M_AN_ARADDR ),
    .S_ARVALID  (eps.M_AN_ARVALID),
    .S_ARREADY  (eps.M_AN_ARREADY),
    .S_RDATA    (eps.M_AN_RDATA  ),
    .S_RRESP    (eps.M_AN_RRESP  ),
    .S_RVALID   (eps.M_AN_RVALID ),
    .S_RREADY   (eps.M_AN_RREADY ),

    .TIME       (48'b0),
    .TRIG       (1'b0),
    .S_DN_TVALID (ex.S_TVALID && ex.S_TREADY),
    .S_DN_TDATA  (ex.S_TDATA ),
    .S_DN_TSTRB  (ex.S_TSTRB ),
    .S_DN_TLAST  (ex.S_TLAST ),
    .S_UP_TVALID (ex.M_TVALID && ex.M_TREADY),
    .S_UP_TDATA  (ex.M_TDATA ),
    .S_UP_TSTRB  (ex.M_TSTRB ),
    .S_UP_TLAST  (ex.M_TLAST ),

    .M_DN_AWID     (eps.S0_AN_AWID),
    .M_DN_AWADDR   (eps.S0_AN_AWADDR ),
    .M_DN_AWLEN    (eps.S0_AN_AWLEN  ),
    .M_DN_AWSIZE   (eps.S0_AN_AWSIZE ),
    .M_DN_AWBURST  (eps.S0_AN_AWBURST),
    .M_DN_AWVALID  (eps.S0_AN_AWVALID),
    .M_DN_AWREADY  (eps.S0_AN_AWREADY),
    .M_DN_WDATA    (eps.S0_AN_WDATA  ),
    .M_DN_WSTRB    (eps.S0_AN_WSTRB  ),
    .M_DN_WLAST    (eps.S0_AN_WLAST  ),
    .M_DN_WVALID   (eps.S0_AN_WVALID ),
    .M_DN_WREADY   (eps.S0_AN_WREADY ),
    .M_DN_BID      (eps.S0_AN_BID),
    .M_DN_BRESP    (eps.S0_AN_BRESP  ),
    .M_DN_BVALID   (eps.S0_AN_BVALID ),
    .M_DN_BREADY   (eps.S0_AN_BREADY ),

    .M_UP_AWID     (eps.S1_AN_AWID),
    .M_UP_AWADDR   (eps.S1_AN_AWADDR ),
    .M_UP_AWLEN    (eps.S1_AN_AWLEN  ),
    .M_UP_AWSIZE   (eps.S1_AN_AWSIZE ),
    .M_UP_AWBURST  (eps.S1_AN_AWBURST),
    .M_UP_AWVALID  (eps.S1_AN_AWVALID),
    .M_UP_AWREADY  (eps.S1_AN_AWREADY),
    .M_UP_WDATA    (eps.S1_AN_WDATA  ),
    .M_UP_WSTRB    (eps.S1_AN_WSTRB  ),
    .M_UP_WLAST    (eps.S1_AN_WLAST  ),
    .M_UP_WVALID   (eps.S1_AN_WVALID ),
    .M_UP_WREADY   (eps.S1_AN_WREADY ),
    .M_UP_BID      (eps.S1_AN_BID),
    .M_UP_BRESP    (eps.S1_AN_BRESP  ),
    .M_UP_BVALID   (eps.S1_AN_BVALID ),
    .M_UP_BREADY   (eps.S1_AN_BREADY )
  );

  initial 
  begin
    SYS_CLK = 1'b1;
    $display("[%t] : System Reset Asserted...", $realtime);
    SYS_RST = 1'b1;
    for (i = 0; i < 500; i = i + 1)
      @(posedge SYS_CLK);
    $display("[%t] : System Reset De-asserted...", $realtime);
    SYS_RST = 1'b0;
  end

  always #2500 SYS_CLK = ~SYS_CLK;

  initial 
  begin
    // Release the reset on the posedge of the clk.
    @(posedge ACLK);
    wait(ARESETn === 0) @(posedge ACLK);
    wait(ARESETn === 1) @(posedge ACLK);
    @(posedge ACLK);
    @(posedge ACLK);
    @(posedge ACLK);
    @(posedge ACLK);
    @(posedge ACLK);
    @(posedge ACLK);
    @(posedge ACLK);
    @(posedge ACLK);
    @(posedge ACLK);
    @(posedge ACLK);
    @(posedge ACLK);
    @(posedge ACLK);
    @(posedge ACLK);
    StartSoftware;
    $stop;
   end

  
task automatic CHECK_RESPONSE_OKAY;
   input [1:0] response;
   begin
      if (response !== `RESPONSE_OKAY) begin
         $display("TESTBENCH ERROR! Response is not OKAY",
                  "\n expected = 0x%h",`RESPONSE_OKAY,
                  "\n actual   = 0x%h",response);
         $stop;
      end
   end
endtask

task RegReadByte;
  input int Address;
  output byte Data;
  reg [31:0] Value;
  reg [1:0] Response;
  begin
    eps.axi4_lite_master.axi4_lite_master.cdn_axi4_lite_master_bfm_inst.READ_BURST(Address, 3'b0, Value, Response);
    CHECK_RESPONSE_OKAY(Response);
    case (Address[1:0])
      2'b00: Data = Value[7:0];
      2'b01: Data = Value[15:8];
      2'b10: Data = Value[23:16];
      2'b11: Data = Value[31:24];
    endcase
  end
endtask 

task RegReadWord;
  input int Address;
  output shortint Data;
  reg [31:0] Value;
  reg [1:0] Response;
  begin
    if (Address[0])
    begin
      $display("Address is not Word Aligned");
      $stop;
    end
    eps.axi4_lite_master.axi4_lite_master.cdn_axi4_lite_master_bfm_inst.READ_BURST(Address, 3'b0, Value, Response);
    CHECK_RESPONSE_OKAY(Response);
    case (Address[0])
      1'b0: Data = Value[15:0];
      1'b1: Data = Value[31:16];
    endcase
  end
endtask

task RegReadDWord;
  input int Address;
  output int Data;
  reg [31:0] Value;
  reg [1:0] Response;
  begin
    if (Address[1:0])
    begin
      $display("Address is not DWord Aligned");
      $stop;
    end
    eps.axi4_lite_master.axi4_lite_master.cdn_axi4_lite_master_bfm_inst.READ_BURST(Address, 3'b0, Value, Response);
    CHECK_RESPONSE_OKAY(Response);
    Data = Value;
  end
endtask

task RegWriteByte;
  input int Address;
  input byte Data;
  reg [1:0] Response;
  begin
    eps.axi4_lite_master.axi4_lite_master.cdn_axi4_lite_master_bfm_inst.WRITE_BURST_CONCURRENT(Address, 3'b0, Data, 1, Response);
    CHECK_RESPONSE_OKAY(Response);
  end
endtask

task RegWriteWord;
  input int Address;
  input shortint Data;
  reg [1:0] Response;
  begin
    if (Address[0])
    begin
      $display("Address is not Word Aligned");
      $stop;
    end
    eps.axi4_lite_master.axi4_lite_master.cdn_axi4_lite_master_bfm_inst.WRITE_BURST_CONCURRENT(Address, 3'b0, Data, 2, Response);
    CHECK_RESPONSE_OKAY(Response);
  end
endtask

task RegWriteDWord;
  input int Address;
  input int Data;
  reg [1:0] Response;
  begin
    if (Address[1:0])
    begin
      $display("Address is not DWord Aligned");
      $stop;
    end
    eps.axi4_lite_master.axi4_lite_master.cdn_axi4_lite_master_bfm_inst.WRITE_BURST_CONCURRENT(Address, 3'b0, Data, 4, Response);
    CHECK_RESPONSE_OKAY(Response);
  end
endtask

task MemRead;
  input int Address;
  input int Bytes;
  output byte Data[4096];
  reg [8191 : 0] RData;
  reg [`M1_USER_BUS_WIDTH * 256 - 1 : 0] RUser;
  reg [1:0] Response;
  int Remaining;
  int Length;
  int i, j;
begin
  Remaining = Bytes/4;
  j=0;
  while (Remaining)
  begin
    Length = (Remaining < 256) ? Remaining : 256;
    eps.axi4_master.axi4_master.cdn_axi4_master_bfm_inst.READ_BURST(0, Address, Length - 1, 3'b010, `BURST_TYPE_INCR, `LOCK_TYPE_NORMAL,
                     0, 0, 0, 0, 0, RData, Response, RUser);
    CHECK_RESPONSE_OKAY(Response);
    Address += (Length * 4);
    Remaining -= Length;
    for (i=0; i<4*Length; i++, j++)
    begin
      Data[j] = RData[8*i+:8];
    end
  end
end
endtask

task MemWrite;
  input int Address;
  input int Bytes;
  input int Data[1024];
  integer WDataSize;
  reg [32767 : 0] WData;
  reg [`M1_USER_BUS_WIDTH * 256 - 1 : 0] WUser;
  reg [`M1_USER_BUS_WIDTH * 256 - 1 : 0] BUser;
  reg [1:0] Response;
  int Remaining;
  int Length;
  int i, j;
begin
  Remaining = Bytes/4;
  j=0;
  while (Remaining)
  begin
    Length = (Remaining < 256) ? Remaining : 256;
    for(i=0; i<4*Length; i++, j++)
      WData[32*i+:32] = Data[j];
    eps.axi4_master.axi4_master.cdn_axi4_master_bfm_inst.WRITE_BURST(0, Address, Length - 1, 3'b010, `BURST_TYPE_INCR, `LOCK_TYPE_NORMAL,
                      0, 0, WData, 4 * Length, 0, 0, 0, WUser, Response, BUser);
    CHECK_RESPONSE_OKAY(Response);
    Address += 4*Length;
    Remaining -= Length;
  end
end
endtask

task MemReadDW;
  input int Address;
  output int Data;
  reg [8191 : 0] RData;
  reg [`M1_USER_BUS_WIDTH * 256 - 1 : 0] RUser;
  reg [1:0] Response;
begin
  eps.axi4_master.axi4_master.cdn_axi4_master_bfm_inst.READ_BURST(0, Address, 0, 3'b010, `BURST_TYPE_INCR, `LOCK_TYPE_NORMAL,
                     0, 0, 0, 0, 0, RData, Response, RUser);
  CHECK_RESPONSE_OKAY(Response);
  Data = RData[31:0];
end
endtask

task MemWriteDW;
  input int Address;
  input int Data;
  reg [8191 : 0] WData;
  reg [`M1_USER_BUS_WIDTH * 256 - 1 : 0] WUser;
  reg [`M1_USER_BUS_WIDTH * 256 - 1 : 0] BUser;
  reg [1:0] Response;
begin
  WData[31:0] = Data;
  eps.axi4_master.axi4_master.cdn_axi4_master_bfm_inst.WRITE_BURST(0, Address, 0, 3'b010, `BURST_TYPE_INCR, `LOCK_TYPE_NORMAL,
                      0, 0, WData, 4, 0, 0, 0, WUser, Response, BUser);
  CHECK_RESPONSE_OKAY(Response);
end
endtask
 
endmodule // tb

